package demo.utils;

import demo.vo.User;
import org.junit.Test;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.util.Arrays;
import java.util.Map;

/**
 * Spring Expression Language (SpEL)。SpEL 是一个功能强大、能在运行时与对象图进行交互的表达式语言。
 * SpEL 表达式以 #{ 开始，以 } 结束。它的核心在于 “查询和操作对象图” 。它能够：
 * <li>访问属性、方法、构造函数：像在 Java 代码中一样访问对象的属性或调用方法。
 * <li>操作数组、列表、映射：通过 [index] 或 [key] 来访问集合元素。
 * <li>进行逻辑和数学运算：支持关系运算符（>, ==, lt, gt 等）、逻辑运算符（and, or, !）、数学运算符（+, -, *, /, %）等。
 * <li>提供正则表达式匹配：使用 matches 操作符。
 * <li>集合投影和选择：从集合中提取特定数据或进行过滤。
 * <li>使用模板表达式：将字面量与动态求值部分混合。
 */
public class SpringEL {

    /**
     * 字面量表达式
     */
    @Test
    public void test1() {

        // 字符串
        ExpressionParser parser = new SpelExpressionParser();
        String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); // 注意单引号
        System.out.println(helloWorld);
        Integer number = (Integer) parser.parseExpression("100").getValue();
        System.out.println(number);

        Double avogadroNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();
        System.out.println(avogadroNumber);

        Boolean trueValue = (Boolean) parser.parseExpression("true").getValue();
        System.out.println(trueValue);

        Object nullValue = parser.parseExpression("null").getValue();
        System.out.println(nullValue);


    }

    /**
     * 访问属性、数组、列表、映射
     */
    @Test
    public void testObject() {
        ExpressionParser parser = new SpelExpressionParser();

        User user = new User();
        user.setName("成吉思汗");
        user.setAge(38);
        user.setLovers(Arrays.asList("射大雕", "征服世界"));
        user.setInfo(null);

        // 访问属性
        String name = (String) parser.parseExpression("name").getValue(user);
        System.out.println(name);
        // 等价于
        name = (String) parser.parseExpression("getName()").getValue(user);
        System.out.println(name);

        // 访问数组/列表
        String lover0 = (String) parser.parseExpression("lovers[0]").getValue(user);
        System.out.println(lover0);

        //安全导航操作符 (?.)
        try {
            String aa = (String) parser.parseExpression("info.['aa']").getValue(user);
            System.out.println(aa);
        } catch (SpelEvaluationException e) {
            System.err.println(e.getMessage());
        }
        String aa = (String) parser.parseExpression("info?.['aa']").getValue(user);
        System.out.println(aa);

    }

    /**
     * 访问映射
     */
    @Test
    public void testMap() {

        Map<String, String> places = Map.of("birth", "Smiljan", "work", "New York");
        EvaluationContext context = new StandardEvaluationContext(places);

        ExpressionParser parser = new SpelExpressionParser();
        String birthPlace = (String) parser.parseExpression("['birth']").getValue(context); // "Smiljan"
        System.out.println(birthPlace);
        // 或者，如果键是简单字符串，也可以用
        String workPlace = (String) parser.parseExpression("[work]").getValue(context); // "New York"
        System.out.println(workPlace);

    }

    /**
     * 操作符
     */
    @Test
    public void testOperator() {
        ExpressionParser parser = new SpelExpressionParser();

        // 关系运算符： < ( lt ), > ( gt ), <= ( le ), >= ( ge ), == ( eq ), != ( ne )

        Boolean isTrue = parser.parseExpression("2 > 1").getValue(Boolean.class); // true
        System.out.println(isTrue);
        isTrue = parser.parseExpression("'foo' == 'foo'").getValue(Boolean.class); // true
        System.out.println(isTrue);

        // 逻辑运算符： and ( && ), or ( || ), not ( ! )

        isTrue = parser.parseExpression("true and false").getValue(Boolean.class); // false
        System.out.println(isTrue);
        //  数学运算符： +, -, *, /, %, ^ (幂)

        Integer result = parser.parseExpression("(2 + 3) * 4").getValue(Integer.class); // 20
        System.out.println(result);
    }
}
